package com.sky.service.impl;

import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.OrderDetail;
import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.service.WorkspaceService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {

	@Autowired
	private OrderMapper orderMapper;
	@Autowired
	private UserMapper userMapper;
	@Autowired
	private WorkspaceService workspaceService;

	/**
	 * 根据时间区间统计营业额
	 *
	 * @param begin
	 * @param end
	 * @return
	 */
	public TurnoverReportVO getTurnover(LocalDate begin, LocalDate end) {
		//创建日期列表，原来存储LocalDate类型的数据
		List<LocalDate> dateList = new ArrayList<>();
		//将开始时间存入列表的第一个位置，之后不断往后推一天
		dateList.add(begin);

		//循环计算日期列表，当不等于结束时间时，不断将符合要求的日期加入列表
		while (!begin.equals(end)) {
			//日期计算，获得指定日期后1天的日期，开始时间不断往后移动一天
			begin = begin.plusDays(1);
			//将符合要求的日期加入列表
			dateList.add(begin);
		}

		//此时dateList列表中存储了开始时间和结束时间之间的所有日期（年月日格式）
		//定义一个存储营业额的列表，循环遍历dateList列表，查询每天的营业额，并存储到turnoverList列表中
		List<Double> turnoverList = new ArrayList<>();
		//根据日期列表查询每天的营业额，并存储到turnoverList列表中
		for (LocalDate date : dateList) {
			//获得指定日期的开始时间和结束时间，分别是一天的开始和一天的结束时间
			//将Date类型转换为LocalDateTime类型
			//LocalDateTime.of(date, LocalTime.MIN)：获得指定日期的开始时间
			//LocalDateTime.of(date, LocalTime.MAX)：获得指定日期的结束时间
			LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
			LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
			//查询指定日期范围内的订单，状态为已完成
			Map map = new HashMap();
			//将查询条件放入map中
			map.put("status", Orders.COMPLETED);
			map.put("begin", beginTime);
			map.put("end", endTime);
			//调用订单Mapper的sumByMap方法，获得指定日期范围内的订单总金额
			Double turnover = orderMapper.sumByMap(map);
			//将营业额加入turnoverList列表中，如果turnover为null，则默认为0.0
			turnover = turnover == null ? 0.0 : turnover;
			//将该天的营业额加入turnoverList列表中
			turnoverList.add(turnover);
		}
		//经过遍历后，turnoverList列表中存储了开始时间和结束时间之间的所有日期的营业额
		//将日期列表和营业额列表转换为字符串，返回给前端

		//数据封装，StringUtils.join方法用于将列表中的元素转换为字符串，并用指定分隔符连接起来
		return TurnoverReportVO.builder()//创建TurnoverReportVO对象
				.dateList(StringUtils.join(dateList, ","))
				.turnoverList(StringUtils.join(turnoverList, ","))
				.build();
	}

	/**
	 * 用户数据统计
	 *
	 * @param begin
	 * @param end
	 * @return
	 */
	@Override
	public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
		//创建日期列表，原来存储LocalDate类型的数据
		List<LocalDate> dateList = new ArrayList<>();
		//将开始时间存入列表的第一个位置，之后不断往后推一天
		dateList.add(begin);

		//循环计算日期列表，当不等于结束时间时，不断将符合要求的日期加入列表
		while (!begin.equals(end)) {
			//日期计算，获得指定日期后1天的日期，开始时间不断往后移动一天
			begin = begin.plusDays(1);
			//将符合要求的日期加入列表
			dateList.add(begin);
		}
		//此时dateList列表中存储了开始时间和结束时间之间的所有日期（年月日格式）

		List<Long> totalUserList = new ArrayList<>();
		List<Long> newUserList = new ArrayList<>();
		//循环遍历dateList列表，查询每天的用户量，并存储到totalUserList和newUserList列表中
		for (LocalDate localDate : dateList) {
			//获得指定日期的开始时间和结束时间，分别是一天的开始和一天的结束时间
			//将Date类型转换为LocalDateTime类型
			//LocalDateTime.of(date, LocalTime.MIN)：获得指定日期的开始时间
			//LocalDateTime.of(date, LocalTime.MAX)：获得指定日期的结束时间
			LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);
			LocalDateTime endTime = LocalDateTime.of(localDate, LocalTime.MAX);
			//求出指定日期的用户总量，然后加进totalUserList中
			//select count(*) from user where create_time < endTime
			Long totalUser = userMapper.selectTotalUser(endTime);
			totalUserList.add(totalUser);
			//求出指定日期的新增用户，然后加进newUserList
			//select count(*) from user where create_time > beginTime and create_time < endTime
			Long newUser = userMapper.selectNewUser(beginTime, endTime);
			newUserList.add(newUser);
		}

		//封装一个UserReportVO，返回给前端
		return UserReportVO.builder()
				.dateList(String.valueOf(dateList))
				.totalUserList(String.valueOf(totalUserList))
				.newUserList(String.valueOf(newUserList))
				.build();
	}

	/**
	 * 订单统计接口
	 *
	 * @param begin
	 * @param end
	 * @return
	 */
	@Override
	public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {
		Integer totalOrderCount = 0;//用来统计订单总数
		Integer validOrderCount = 0;//用来统计有效订单数
		double orderCompletionRate = 0.0;//用来统计订单完成率
		//创建日期列表，原来存储LocalDate类型的数据
		List<LocalDate> dateList = new ArrayList<>();
		//将开始时间存入列表的第一个位置，之后不断往后推一天
		dateList.add(begin);

		//循环计算日期列表，当不等于结束时间时，不断将符合要求的日期加入列表
		while (!begin.equals(end)) {
			//日期计算，获得指定日期后1天的日期，开始时间不断往后移动一天
			begin = begin.plusDays(1);
			//将符合要求的日期加入列表
			dateList.add(begin);
		}
		//此时dateList列表中存储了开始时间和结束时间之间的所有日期（年月日格式）
		List<Integer> orderCountList = new ArrayList<>();
		List<Integer> validOrderCountList = new ArrayList<>();
		for (LocalDate localDate : dateList) {
			//获得指定日期的开始时间和结束时间，分别是一天的开始和一天的结束时间
			//将Date类型转换为LocalDateTime类型
			//LocalDateTime.of(date, LocalTime.MIN)：获得指定日期的开始时间
			//LocalDateTime.of(date, LocalTime.MAX)：获得指定日期的结束时间
			LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);
			LocalDateTime endTime = LocalDateTime.of(localDate, LocalTime.MAX);
			//求当天的订单数；然后加入到orderCountList中
			//select count(*) from orders where order_time between beginTime and endTime;
			Integer orderCount = orderMapper.selectOrderCount(beginTime, endTime);
			orderCountList.add(orderCount);
			//求当天的有效订单数；然后加入到validOrderCountList列表中
			//select count(*) from orders where order_time (between beginTime and endTime) and status = Orders.COMPLETED(5)
			Integer validOrder = orderMapper.selectOrderCountAndStatus(beginTime, endTime);
			validOrderCountList.add(validOrder);
			//求该范围的订单总数，赋值给totalOrderCount
			totalOrderCount += orderCount;
			//求该范围的有效订单数，赋值给validOrderCount
			validOrderCount += validOrder;
		}
		//根据有效订单数和订单总数，求该范围内的订单完成率
		if (totalOrderCount > 0) {
			orderCompletionRate = (double) validOrderCount / totalOrderCount;
		}

		//封装VO对象，返回给前端
		return OrderReportVO.builder()
				.dateList(String.valueOf(dateList))
				.orderCountList(String.valueOf(orderCountList))
				.validOrderCountList(String.valueOf(validOrderCountList))
				.totalOrderCount(totalOrderCount)
				.validOrderCount(validOrderCount)
				.orderCompletionRate(orderCompletionRate)
				.build();
	}

	/**
	 * 查询指定时间区间内的销量排名top10
	 * @param begin
	 * @param end
	 * @return
	 * */
	public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end){
		//获得指定日期的开始时间和结束时间，分别是一天的开始和一天的结束时间
		LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
		LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);
		//调用订单Mapper的getSalesTop10方法，获得指定日期范围内的销量排名top10的商品列表
		List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);

		//将商品列表转换为字符串，返回给前端
		String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList()),",");
		String numberList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList()),",");

		//封装VO对象，返回给前端
		return SalesTop10ReportVO.builder()
				.nameList(nameList)
				.numberList(numberList)
				.build();
	}

	/**导出近30天的运营数据报表
	 * @param response
	 **/
	/**
 * 导出业务数据到Excel文件的方法
 * 该方法根据过去三十天的数据生成一个Excel报表，并将其作为响应返回给客户端
 */
public void exportBusinessData(HttpServletResponse response) {
		//计算往前三十天的日期和昨天的日期
		LocalDate begin = LocalDate.now().minusDays(30);
		LocalDate end = LocalDate.now().minusDays(1);
		//查询概览运营数据，提供给Excel模板文件
		BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(begin,LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));
		//获得模板文件流
		InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
		try {
			//基于提供好的模板文件创建一个新的Excel表格对象
			XSSFWorkbook excel = new XSSFWorkbook(inputStream);
			//获得Excel文件中的一个Sheet页
			XSSFSheet sheet = excel.getSheet("Sheet1");

			sheet.getRow(1).getCell(1).setCellValue(begin + "至" + end);
			//获得第4行
			XSSFRow row = sheet.getRow(3);
			//获取单元格
			row.getCell(2).setCellValue(businessData.getTurnover());
			row.getCell(4).setCellValue(businessData.getOrderCompletionRate());
			row.getCell(6).setCellValue(businessData.getNewUsers());
			row = sheet.getRow(4);
			row.getCell(2).setCellValue(businessData.getValidOrderCount());
			row.getCell(4).setCellValue(businessData.getUnitPrice());
			for (int i = 0; i < 30; i++) {
				LocalDate date = begin.plusDays(i);
				//准备明细数据
				businessData = workspaceService.getBusinessData(LocalDateTime.of(date,LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));
				row = sheet.getRow(7 + i);
				row.getCell(1).setCellValue(date.toString());
				row.getCell(2).setCellValue(businessData.getTurnover());
				row.getCell(3).setCellValue(businessData.getValidOrderCount());
				row.getCell(4).setCellValue(businessData.getOrderCompletionRate());
				row.getCell(5).setCellValue(businessData.getUnitPrice());
				row.getCell(6).setCellValue(businessData.getNewUsers());
			}
			//通过输出流将文件下载到客户端浏览器中
			ServletOutputStream out = response.getOutputStream();
			excel.write(out);
			//关闭资源
			out.flush();
			out.close();
			excel.close();

		}catch (IOException e){
			e.printStackTrace();
		}
	}

}